package com.ambitor.grass.util.data;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import net.sf.json.JSONArray;
import net.sf.json.JSONObject;

@SuppressWarnings( { "serial", "unchecked" })
public class DatasetList extends ArrayList implements IDataset {

	private String serializableId;
	private String serializablePath;
	private boolean serializable;
	private boolean batchSerializable;
	private int batchPageSize;
	private int batchPageCount;
	private int count = -1;

	/**
	 * construct function
	 */
	public DatasetList() {
		super();
	}

	/**
	 * construct function
	 * 
	 * @param list
	 */
	public DatasetList(List list) {
		addAll(list);
	}

	/**
	 * construct function
	 * 
	 * @param value
	 */
	public DatasetList(String value) {
		List list = DataHelper.strToList(value);
		if (list != null)
			addAll(list);
	}

	/**
	 * get serializable id
	 * 
	 * @return String
	 */
	public String getSerializableId() {
		return serializableId;
	}

	/**
	 * set serializable id
	 * 
	 * @param serializableId
	 */
	public void setSerializableId(String serializableId) {
		this.serializableId = serializableId;
	}

	/**
	 * get serializable path
	 * 
	 * @return String
	 */
	public String getSerializablePath() {
		return serializablePath;
	}

	/**
	 * set serializable path
	 * 
	 * @param serializablePath
	 */
	public void setSerializablePath(String serializablePath) {
		this.serializablePath = serializablePath;
	}

	/**
	 * is serializable
	 * 
	 * @return boolean
	 */
	public boolean isSerializable() {
		return serializable;
	}

	/**
	 * set serializable
	 * 
	 * @param serializable
	 */
	public void setSerializable(boolean serializable) {
		this.serializable = serializable;
	}

	/**
	 * is batch serializable
	 * 
	 * @return boolean
	 */
	public boolean isBatchSerializable() {
		return batchSerializable;
	}

	/**
	 * set batch serializable
	 * 
	 * @param batchSerializable
	 */
	public void setBatchSerializable(boolean batchSerializable) {
		this.batchSerializable = batchSerializable;
	}

	/**
	 * set batch page size
	 * 
	 * @return int
	 */
	public int getBatchPageSize() {
		return batchPageSize;
	}

	/**
	 * set batch page size
	 * 
	 * @param batchPageSize
	 */
	public void setBatchPageSize(int batchPageSize) {
		this.batchPageSize = batchPageSize;
	}

	/**
	 * set batch page count
	 * 
	 * @return int
	 */
	public int getBatchPageCount() {
		return batchPageCount;
	}

	/**
	 * set batch page count
	 * 
	 * @param batchPageSize
	 */
	public void setBatchPageCount(int batchPageCount) {
		this.batchPageCount = batchPageCount;
	}

	/**
	 * get object
	 * 
	 * @param index
	 * @return Object
	 */
	public Object get(int index) {
		Object value = super.get(index);
		if (value == null)
			return null;

		if (value instanceof JSONObject) {
			IData data = new DataMap();
			data.putAll((JSONObject) value);
			value = data;
		} else if (value instanceof JSONArray) {
			IDataset dataset = new DatasetList();
			dataset.addAll((JSONArray) value);
			value = dataset;
		}

		this.set(index, value);

		return value;
	}

	/**
	 * get object
	 * 
	 * @param index
	 * @param name
	 * @return Object
	 */
	public Object get(int index, String name) {
		IData data = (IData) get(index);
		return data == null ? null : data.get(name);
	}

	/**
	 * get object
	 * 
	 * @param index
	 * @param name
	 * @param def
	 * @return Object
	 */
	public Object get(int index, String name, Object def) {
		Object value = get(index, name);
		return value == null ? def : value;
	}

	/**
	 * get data
	 * 
	 * @param index
	 * @return IData
	 */
	public IData getData(int index) throws Exception {
		return (IData) get(index);
	}

	/**
	 * get names
	 * 
	 * @return String[]
	 */
	public String[] getNames() {
		return size() > 0 ? ((IData) get(0)).getNames() : null;
	}

	/**
	 * to data
	 * 
	 * @return IData
	 */
	public IData toData() throws Exception {
		IData data = new DataMap();

		Iterator it = iterator();
		while (it.hasNext()) {
			IData element = (IData) it.next();
			Iterator iterator = element.keySet().iterator();
			while (iterator.hasNext()) {
				String key = (String) iterator.next();
				if (data.containsKey(key)) {
					IDataset list = (IDataset) data.get(key);
					list.add(element.get(key));
				} else {
					IDataset list = new DatasetList();
					list.add(element.get(key));
					data.put(key, list);
				}
			}
		}

		if ("".equals(data.get("X_RECORDNUM", "")))
			data.put("X_RECORDNUM", String.valueOf(size()));

		return data;
	}

	/**
	 * filter for example:filter="KEY1=VALUE1,KEY2=VALUE2"，表达式之间的关系是and
	 * 或者filter="KEY1~VALUE1;KEY2~VALUE2"~代表进行似于sql中的like比较，表达式之间的关系是or
	 * 
	 * @author 肖亚改写
	 * @param filter
	 * @return IDataset
	 * @throws Exception
	 */
	public IDataset filter(String filter) throws Exception {
		if (filter == null || "".equals(filter))
			return this;

		IData ftdt = new DataMap();
		boolean isAnd = true;
		String[] fts = null;
		if (filter.indexOf('~') != -1) {
			fts = filter.split(";");
			isAnd = false;
		} else {
			fts = filter.split(",");
		}

		if (isAnd) {
			for (int i = 0; i < fts.length; i++) {
				String[] ft = fts[i].split("=");
				ftdt.put(ft[0], ft[1]);
			}
		} else {
			for (int i = 0; i < fts.length; i++) {
				String[] ft = fts[i].split("~");
				ftdt.put(ft[0] + "@" + i, ft[1]);
			}
		}

		IDataset subset = new DatasetList();
		if (isAnd) {
			for (int i = 0; i < size(); i++) {
				IData subdata = (IData) get(i);
				boolean include = true;
				String[] ftdtNames = ftdt.getNames();
				for (int j = 0; j < ftdtNames.length; j++) {
					String subvalue = subdata.getString(ftdtNames[j]);
					if (subvalue == null
							|| !subvalue.equals(ftdt.get(ftdtNames[j]))) {
						include = false;
						break;
					}
				}
				if (include)
					subset.add(subdata);
			}
		} else {
			for (int i = 0; i < size(); i++) {
				IData subdata = getData(i);
				boolean include = false;
				String[] ftdtNames = ftdt.getNames();
				for (int j = 0; j < ftdtNames.length; j++) {
					String realKeyName = ftdtNames[j].substring(0, ftdtNames[j]
							.indexOf("@"));
					String subvalue = (String) subdata.get(realKeyName);
					if (subvalue.contains((ftdt.getString(ftdtNames[j])))) {
						include = true;
						break;
					}
				}
				if (include)
					subset.add(subdata);
			}
		}

		return subset;
	}

	/**
	 * filter for example:filter="KEY1=VALUE1,KEY2=VALUE2" 原版程序
	 * 
	 * @param filter
	 * @return IDataset
	 * @throws Exception
	 */
	/*
	 * public IDataset filter(String filter) throws Exception { if (filter ==
	 * null || "".equals(filter)) return this;
	 * 
	 * IData ftdt = new DataMap(); String[] fts = filter.split(","); for (int i
	 * = 0; i < fts.length; i++) { String[] ft = fts[i].split("=");
	 * ftdt.put(ft[0], ft[1]); }
	 * 
	 * IDataset subset = new DatasetList(); for (int i = 0; i < size(); i++) {
	 * IData subdata = (IData) get(i); boolean include = true; String[]
	 * ftdtNames = ftdt.getNames(); for (int j = 0; j < ftdtNames.length; j++) {
	 * String subvalue = (String) subdata.get(ftdtNames[j]); if (subvalue ==
	 * null || !subvalue.equals(ftdt.get(ftdtNames[j]))) { include = false;
	 * break; } } if (include) subset.add(subdata); }
	 * 
	 * return subset; }
	 */

	/**
	 * distinct
	 * 
	 * @param fieldNames
	 * @param token
	 * @return
	 * @throws Exception
	 */
	public IDataset distinct(String fieldNames, String token) throws Exception {
		if ("".equals(fieldNames))
			return this;

		List fieldValues = new ArrayList();
		IDataset subset = new DatasetList();
		String theToken = token == null || "".equals(token) ? "," : token;

		String[] keys = fieldNames.split(theToken);
		for (int i = 0; i < size(); i++) {
			String fieldValue = "";
			for (int j = 0; j < keys.length; j++) {
				fieldValue += (String) get(i, keys[j]) + theToken;
			}
			if ("".equals(fieldValue))
				continue;
			if (!fieldValues.contains(fieldValue)) {
				fieldValues.add(fieldValue);
				subset.add(get(i));
			}
		}
		return subset;
	}

	/**
	 * distinct
	 * 
	 * @param fieldName
	 * @return IDataset
	 * @throws Exception
	 */
	public IDataset distinct(String fieldNames) throws Exception {
		return distinct(fieldNames, ",");
	}

	/**
	 * get count
	 * 
	 * @return int
	 */
	public int count() {
		return count == -1 ? size() : count;
	}

	/**
	 * set count
	 * 
	 * @param count
	 */
	public void setCount(int count) {
		this.count = count;
	}

	/**
	 * sort single (default ascend)
	 */
	public void sort(String key, int keyType) {
		sort(key, keyType, IDataset.ORDER_ASCEND);
	}

	/**
	 * sort single
	 */
	public void sort(String key, int keyType, int order) {
		Object[] datas = (Object[]) this.toArray();
		DataComparator c = new DataComparator(key, keyType, order);
		Arrays.sort(datas, c);

		List list = Arrays.asList(datas);

		this.clear();
		this.addAll(list);
	}

	/**
	 * sort double (default ascend)
	 */
	public void sort(String key1, int keyType1, String key2, int keyType2) {
		sort(key1, keyType1, IDataset.ORDER_ASCEND);
		sort(key2, keyType2, IDataset.ORDER_ASCEND, key1, keyType1);
	}

	/**
	 * sort double
	 */
	public void sort(String key1, int keyType1, int order1, String key2,
			int keyType2, int order2) {
		sort(key1, keyType1, order1);
		sort(key2, keyType2, order2, key1, keyType1);
	}

	/**
	 * sort assistant
	 */
	private void sort(String key, int type, int order, String fix, int fixType) {
		Object[] datas = (Object[]) this.toArray();

		DataComparator c = new DataComparator(key, type, order);

		if (fix == null) {
			Arrays.sort(datas, c);
		} else {
			int[] marks = Anchor.mark(this, fix, fixType);

			for (int pre = 0, i = 1; i < marks.length; i++) {
				Arrays.sort(datas, pre, marks[i], c);
				pre = marks[i];
			}
		}
		List list = Arrays.asList(datas);

		this.clear();
		this.addAll(list);
	}

	/**
	 * to string
	 * 
	 * @return String
	 */
	public String toString() {
		StringBuffer str = new StringBuffer();
		str.append("[");

		Iterator it = iterator();
		while (it.hasNext()) {
			Object value = it.next();
			if (value == null) {
				str.append("\"\"");
			} else if (value instanceof Map || value instanceof List) {
				str.append(value);
			} else {
				str.append("\"" + value + "\"");
			}
			if (it.hasNext())
				str.append(",");
		}

		str.append("]");
		return str.toString();
	}

	/**
	 * to wade string
	 * 
	 * @return String
	 */
	public String toWadeString() {
		StringBuffer str = new StringBuffer();
		str.append("[");

		Iterator it = iterator();
		while (it.hasNext()) {
			Object value = it.next();
			if (value == null) {
				str.append("\"\"");
			} else if (value instanceof JSONObject) {
				IData object = new DataMap();
				object.putAll((Map) value);
				str.append(object.toWadeString());
			} else if (value instanceof JSONArray) {
				IDataset object = new DatasetList();
				object.addAll((List) value);
				str.append(object.toWadeString());
			} else if (value instanceof Map) {
				str.append(((IData) value).toWadeString());
			} else if (value instanceof List) {
				str.append(((IDataset) value).toWadeString());
			} else {
				str.append("\"" + value + "\"");
			}
			if (it.hasNext())
				str.append(", ");
		}

		str.append("]");
		return str.toString();
	}

	/**
	 * to bude string
	 * 
	 * @return String
	 */
	public String toBudeString() {
		StringBuffer str = new StringBuffer();
		str.append("[");

		Iterator it = iterator();
		while (it.hasNext()) {
			Object value = it.next();
			if (value == null) {
				str.append("\"\"");
			} else if (value instanceof JSONObject) {
				IData object = new DataMap();
				object.putAll((Map) value);
				str.append(object.toBudeString());
			} else if (value instanceof JSONArray) {
				IDataset object = new DatasetList();
				object.addAll((List) value);
				str.append(object.toBudeString());
			} else if (value instanceof Map) {
				str.append(((IData) value).toBudeString());
			} else if (value instanceof List) {
				str.append(((IDataset) value).toBudeString());
			} else {
				str.append("\""
						+ value.toString().replaceAll("\\\\", "\\\\\\\\")
								.replaceAll("\\\"", "\\\\\"") + "\"");
			}
			if (it.hasNext())
				str.append(", ");
		}

		str.append("]");
		return str.toString();
	}

}